
/**
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Autogenerated file -- DO NOT EDIT!

#include "source_languages.h"
#include "verification/absint/panda_types.h"
#include "verification/jobs/job.h"

#ifndef VERIFIER_LANG_SPECIFICS_H
#define VERIFIER_LANG_SPECIFICS_H

namespace panda::verifier {

% Common::plugins.each do |plugin_lang, plugin_opts|
% if plugin_lang == "JAVA"

    inline TypeRelationship GetRelationship(const Type &type1, const Type &type2)
    {
        if (type1 == type2) {
            return TypeRelationship::SAME;
        } else if (type1 <= type2) {
            return TypeRelationship::DESCENDANT;
        } else {
            return TypeRelationship::OTHER;
        }
    }

    inline TypeRelationship GetRelationship(const Job &job, PandaTypes &types,
                                            const CachedClass &class1,
                                            const CachedClass &class2)
    {
        auto lang = job.JobCachedMethod().GetSourceLang();
        if (lang == panda::panda_file::SourceLang::JAVA_8) {
            TypeRelationship relationship = GetRelationship(types.TypeOf(class1), types.TypeOf(class2));
            if (relationship != TypeRelationship::SAME && class1.IsSamePackage(class2)) {
                // Member access levels strength:
                // SAME (same class) << NEIGHBOUR (same package) << DESCENDANT (subclass) << OTHER
                return TypeRelationship::NEIGHBOUR;
            }
            return relationship;
        }
        // For non-java language context return TypeRelationship::SAME.
        // Though it is not semantically correct, the TypeRelationship is used in access
        // checks only, and SAME helps to pass the access checks for non-java context.
        return TypeRelationship::SAME;
    }

    // works for both fields and methods
    template <typename T>
    AccessModifier GetAccessMode(const T *x)
    {
        if (x->flags[T::Flag::PRIVATE]) {
            return AccessModifier::PRIVATE;
        } else if (x->flags[T::Flag::PROTECTED]) {
            return AccessModifier::PROTECTED;
        } else if (x->flags[T::Flag::PUBLIC]) {
            return AccessModifier::PUBLIC;
        } else {
            return AccessModifier::PACKAGE;
        }
    }

% end
% end

    inline const CheckResult& CheckFieldAccessViolation([[maybe_unused]] const CachedField *field,
                                                        [[maybe_unused]] const Job &currentJob,
                                                        [[maybe_unused]] PandaTypes &types) {
% Common::plugins.each do |plugin_lang, plugin_opts|
% if plugin_lang == "JAVA"
        auto relation = GetRelationship(currentJob, types, currentJob.JobCachedMethod().klass, field->klass);
        AccessModifier access_mode = GetAccessMode(field);
        return panda::verifier::CheckFieldAccess(relation, access_mode);
% end
% end
        return CheckResult::ok;
    }

    inline const CheckResult& CheckMethodAccessViolation([[maybe_unused]] const CachedMethod *method,
                                                         [[maybe_unused]] const Job &currentJob,
                                                         [[maybe_unused]] PandaTypes &types) {
% Common::plugins.each do |plugin_lang, plugin_opts|
% if plugin_lang == "JAVA"
        auto relation = GetRelationship(currentJob, types, currentJob.JobCachedMethod().klass, method->klass);
        auto access_mode = GetAccessMode(method);
        return panda::verifier::CheckCall(relation, access_mode);
% end
% end
        return CheckResult::ok;
    }

    inline const CheckResult& CheckClassAccessViolation([[maybe_unused]] const CachedClass *cached_class,
                                                        [[maybe_unused]] const Job &currentJob,
                                                        [[maybe_unused]] PandaTypes &types) {
% Common::plugins.each do |plugin_lang, plugin_opts|
% if plugin_lang == "JAVA"
        auto relation = GetRelationship(currentJob, types, currentJob.JobCachedMethod().klass, *cached_class);
        if (relation == TypeRelationship::OTHER) {
            auto is_public = cached_class->flags[CachedClass::Flag::PUBLIC];
            if (!is_public) {
                return CheckResult::protected_class;
            }
        }
% end
% end
        return CheckResult::ok;
    }
}

#endif // VERIFIER_LANG_SPECIFICS_H
